package collection;

import exception.CollectionEmptyException;

public class SLinkedList<T> extends ALinkedList<SNode<T>, T>{
	
	public SLinkedList()
	{
		super();
	}
	
	public SLinkedList(SLinkedList<T> other)
	{
		super(other);
	}
	
	@Override
	protected SNode<T> createHead() 
	{
		return new SNode<T>();
	}
	
	@Override
	public SNode<T> createNode(T element) 
	{
		return new SNode<T>(element);
	}
	
	@Override
	public void addFirst(SNode<T> newNode)
	{
		if (isEmpty())
		{
			//since head==tail, it should be the same as addToTail(newNode)
			addLast(newNode);
		}
		else
		{
			newNode.setNext(super.head.getNext());
			super.head.setNext(newNode);
			super.size += 1;
		}
	}
	
	@Override
	public void addLast(SNode<T> newNode)
	{
		newNode.setNext(null);
		super.tail.setNext(newNode);
		super.tail = newNode;
		super.size += 1;
	}
	
	@Override
	public SNode<T> removeFirst()
	{
		if (isEmpty())
		{
			throw new CollectionEmptyException("Linked List");
		}
		
		// get the first node
		SNode<T> firstNode = super.head.getNext();
		
		// there is only one element
		if (firstNode.getNext() == null)
		{
			super.head.setNext(null);
			super.tail = super.head;
		}
		else // there are more than one elements
		{
			super.head.setNext(firstNode.getNext());
		}
		
		super.size -= 1;
		
		firstNode.setNext(null);
		
		return firstNode;
	}
	
	@Override
	public SNode<T> removeLast()
	{
		if (isEmpty())
		{
			throw new CollectionEmptyException("Linked List");
		}
		
		// get the last node
		SNode<T> lastNode = super.tail;
		
		// there is only one element
		if (super.head.getNext() == lastNode)
		{
			super.head.setNext(null);
			super.tail = super.head;
		}
		else // there are more than one elements
		{
			//find the node before the last node
			SNode<T> curNode = super.head.getNext();
			while(curNode.getNext().getNext() != null)
			{
				curNode = curNode.getNext();
			}
			curNode.setNext(null);
			super.tail = curNode;
		}
		
		super.size -= 1;
		
		lastNode.setNext(null);
		
		return lastNode;
	}

	@Override
	public void reverse() 
	{	
		if (size() <=1 )
		{
			return ;
		}
		
		SLinkedList<T> newList = new SLinkedList<T>();
		while(!isEmpty())
		{
			newList.addFirst(removeFirst());
		}
		super.head = newList.head;
		super.tail = newList.tail;
		super.size = newList.size;
	}

	@Override
	public void remove(SNode<T> someNode) 
	{	
		if (isEmpty())
		{
			return ;
		}
		
		SNode<T> firstNode = super.head.getNext();
		if (firstNode == someNode)
		{
			removeFirst();
			return ;
		}
		if (super.tail == someNode)
		{
			removeLast();
			return ;
		}
		
		SNode<T> curNode = firstNode;
		SNode<T> nextNode = curNode.getNext();
		while(nextNode != null )
		{
			if (nextNode == someNode)
			{
				curNode.setNext(nextNode.getNext());
				super.size -= 1;
				
				someNode.setNext(null);
				
				return ;
			}
			
			curNode = curNode.getNext();
			nextNode = curNode.getNext();
		}
	}

	@Override
	public SNode<T> getPrev(SNode<T> someNode) 
	{	
		SNode<T> curNode = super.head;
		SNode<T> nextNode = curNode.getNext();
		while(nextNode != someNode)
		{
			curNode = curNode.getNext();
			nextNode = curNode.getNext();
		}
		
		return (curNode == super.head ? null : curNode);
	}

	@Override
	public SNode<T> getNext(SNode<T> someNode) 
	{	
		return someNode.getNext();
	}
	
	@Override
	public void addBefore(SNode<T> someNode, SNode<T> newNode) 
	{		
		if (super.head.getNext() == someNode)
		{
			addFirst(newNode);
			return ;
		}
		
		SNode<T> prevNode = getPrev(someNode);
		prevNode.setNext(newNode);
		newNode.setNext(someNode);
		super.size += 1;
	}

	@Override
	public void addAfter(SNode<T> someNode, SNode<T> newNode) 
	{	
		if (super.tail == someNode)
		{
			addLast(newNode);
			return ;
		}
		
		newNode.setNext(someNode.getNext());
		someNode.setNext(newNode);
		super.size += 1;
	}
	
}
